﻿using System.Net.Http.Json;
using System.Text.Json.Serialization;
using System.Text.Json;

namespace Digitalmes.Core.Common.Extensions;

public static partial class HttpClientExtensions
{
    private static readonly Lazy<JsonSerializerOptions> JsonSerializerWebOptions = new(() =>
    {
        JsonSerializerOptions options = new(JsonSerializerDefaults.Web);
        options.Converters.Add(new DateTimeOffsetConverterUsingDateTimeParse());
        options.Converters.Add(new DateTimesetConverterUsingDateTimeParse());
        return options;
    });

    /// <summary>
    /// 调用 GET 方法请求指定的资源，以 JSON 对象返回并进行反序列化。
    /// </summary>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="requestUri">请求的资源</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <remarks>若响应错误或解析错误都会抛出异常。</remarks>
    /// <exception cref="HttpRequestException"></exception>
    /// <exception cref="JsonException"></exception>
    public static Task<TValue?> GetJsonAsync<TValue>(this HttpClient client, 
        [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, 
        CancellationToken cancellationToken = default)
    {
        return client.GetFromJsonAsync<TValue>(requestUri, JsonSerializerWebOptions.Value, cancellationToken);
    }

    /// <summary>
    /// 调用 POST 方法请求指定的资源，以 JSON 对象返回并进行反序列化。
    /// </summary>
    /// <typeparam name="TRequest"></typeparam>
    /// <typeparam name="TResponse"></typeparam>
    /// <param name="requestUri">请求的资源</param>
    /// <param name="request">请求数据，会序列化成 JSON 对象。</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <remarks>若响应错误或解析错误都会抛出异常。</remarks>
    /// <exception cref="HttpRequestException"></exception>
    /// <exception cref="JsonException"></exception>
    public static async Task<TResponse?> PostJsonAsync<TRequest, TResponse>(this HttpClient client, 
        [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, 
        TRequest request, 
        CancellationToken cancellationToken = default)
    {
        using var response = await client.PostAsJsonAsync(requestUri, request, cancellationToken);
        response.EnsureSuccessStatusCode();
        var content = await response.Content.ReadAsStreamAsync(cancellationToken);
        return await JsonSerializer.DeserializeAsync<TResponse>(content, JsonSerializerWebOptions.Value, cancellationToken);
    }

    /// <summary>
    /// 调用 PUT 方法请求指定的资源，以 JSON 对象返回并进行反序列化。
    /// </summary>
    /// <typeparam name="TRequest"></typeparam>
    /// <typeparam name="TResponse"></typeparam>
    /// <param name="requestUri">请求的资源</param>
    /// <param name="request">请求数据，会序列化成 JSON 对象。</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <remarks>若响应错误或解析错误都会抛出异常。</remarks>
    /// <exception cref="HttpRequestException"></exception>
    /// <exception cref="JsonException"></exception>
    public static async Task<TResponse?> PutJsonAsync<TRequest, TResponse>(this HttpClient client, 
        [StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, 
        TRequest request, 
        CancellationToken cancellationToken = default)
    {
        using var response = await client.PutAsJsonAsync(requestUri, request, cancellationToken);
        response.EnsureSuccessStatusCode();
        var content = await response.Content.ReadAsStreamAsync(cancellationToken);
        return await JsonSerializer.DeserializeAsync<TResponse>(content, JsonSerializerWebOptions.Value, cancellationToken);
    }

    /// <summary>
    /// JSON DateTimeOffset 转换器
    /// </summary>
    private sealed class DateTimeOffsetConverterUsingDateTimeParse : JsonConverter<DateTimeOffset>
    {
        public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            Debug.Assert(typeToConvert == typeof(DateTimeOffset));
            return DateTimeOffset.Parse(reader.GetString()!);
        }

        public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
        {
            writer.WriteStringValue(value.ToString());
        }
    }

    /// <summary>
    /// JSON DateTime 转换器
    /// </summary>
    private sealed class DateTimesetConverterUsingDateTimeParse : JsonConverter<DateTime>
    {
        public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            Debug.Assert(typeToConvert == typeof(DateTime));
            return DateTime.Parse(reader.GetString()!);
        }

        public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
        {
            writer.WriteStringValue(value.ToString());
        }
    }
}
